<!DOCTYPE html>
<html>
  <head>
    <meta charset='utf-8'>

    <link rel="stylesheet" type="text/css" 
      href="/assets/css/straybirds.css" media="screen" />
    <link rel="stylesheet" type="text/css" 
      href="/assets/css/pygments.css" media="screen" />

    <!-- MathJax Section Start -->

    <script type="text/javascript"
    src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
    </script>
    <script>
        MathJax.Hub.Config({
              tex2jax: {
              skipTags: ['script', 'noscript', 'style', 'textarea', 'pre']
              }
        });
        MathJax.Hub.Queue(function() {
            var all = MathJax.Hub.getAllJax(), i;
            for(i=0; i < all.length; i += 1) {
                all[i].SourceElement().parentNode.className += ' has-jax';
            }
        });
    </script>

    <!-- MathJax Section End -->

    <!-- Google Analytics Start-->
    <script>
  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

  ga('create', 'UA-48100787-1', 'minixalpha.github.io');
  ga('send', 'pageview');

</script>

    <!-- Google Analytics End -->

    <title>web.py 项目架构分析之 googlemodules</title>
  </head>

  <body>
    <div class="container">
      <header>
        <div class="container">
          <h1>
              <a href="/" title="Home Page"> 潇湘夜雨 </a>
          <span class="github-src">
            <a href ="https://github.com/minixalpha/minixalpha.github.io"
               target="_blank"
               title="Fork me on GitHub">
              <img src="/assets/images/GitHub-Mark-Light-32px.png" alt="">
            </a>
          </span>
          </h1>
        </div>
      </header>

      <aside id="left-side">
        <h2> 分类 </h2>
  <ul class="category-list">
      
            
                <li>
                <a href="/categories/计算机系统"> 计算机系统 (3) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/java"> java (1) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/技术"> 技术 (5) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/工具"> 工具 (4) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/科研"> 科研 (5) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/生活"> 生活 (1) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/思想"> 思想 (2) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/c语言"> c语言 (4) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/虚拟机"> 虚拟机 (1) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/英语"> 英语 (8) </a>
                </li>
            
      
            
                <li>
                <a href="/categories/读书"> 读书 (1) </a>
                </li>
            
      
            
                <li style="background-color: #444">
                <a href="/categories/源代码阅读"> 源代码阅读 (10) </a>
                </li>
            
      
  </ul>

      </aside>

      <aside id="right-side">
        <h2> 归档 </h2>
  <ul class="archive-list">
    
    
    
        
        
        
        
            
            <li>
                <a href="/2014/05">
                    2014-05 (1)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2014/04">
                    2014-04 (3)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2014/03">
                    2014-03 (11)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2014/02">
                    2014-02 (6)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2014/01">
                    2014-01 (3)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2013/11">
                    2013-11 (10)
                </a>
            </li>

        
        
    
        
        
        
        
    
        
        
        
        
    
        
        
        
        
            
            <li>
                <a href="/2013/10">
                    2013-10 (3)
                </a>
            </li>

        
        
    
  </ul>

      </aside>

      <article>

<h1>web.py 项目之 googlemodules</h1>

<h2>项目说明</h2>

<p>项目来自 <a href="http://webpy.org/src/">webpy.org</a>, 这是一个真实的在线上运行的
项目: <a href="http://www.googlemodules.com/">Google Modules</a>, 可以上传，下载，
一些模块，还有一些评分，打标签等等功能。(不过这网站挺奇怪的。。。)</p>

<h2>目录树</h2>
<div class="highlight"><pre><code class="text language-text" data-lang="text">src/
    ├── application.py
    ├── forum.py
    ├── config_example.py
    ├── INSTALL
    ├── LICENCE
    ├── app
    │   ├── controllers
    │   ├── helpers
    │   ├── models
    │   └── views
    ├── app_forum
    │   ├── controllers
    │   ├── models
    │   └── views
    ├── data
    ├── public
    │   ├── css
    │   ├── img
    │   │   └── star
    │   ├── js
    │   └── rss
    ├── scripts
    └── sql

18 directories
</code></pre></div>
<p>终于遇到个稍微大一点的项目了，要好好看看。</p>

<p>从目录上看，整个项目分成两个部分，app 和 app_forum,每个部分都使用了
典型的MVC结构，将app分成 controllers, models, views 三大部分。</p>

<p>另外，网站使用的 css, js 文件，图片，也都统一放在了public目录下。</p>

<p>INSTALL 文件描述了如何安装部署项目, 包括在哪里下载项目，哪里下载web.py，如何
配置 lighttpd, 如何配置项目。</p>

<p>config_example.py 文件给了一个配置文件模板，按自己的需要修改其中内容，最后
把文件名改为 config.py 就可以了，其中包括对数据的配置，调试，缓存的开启等等。</p>

<p>LICENCE 文件描述了项目使用的开源协议: GPLv3。</p>

<p>项目使用的脚本放在scripts目录下，创建数据库使用的文件放在了sql目录下。</p>

<h2>代码统计</h2>

<p>先看看代码统计</p>

<p><img src="/assets/blog-images/googlemodules_code_stat.jpg" alt="googlemodules_code_stat.jpg"></p>

<h2>application 模块</h2>

<h3>application.py</h3>
<div class="highlight"><pre><code class="python language-python" data-lang="python"><span class="c">#!/usr/bin/env python</span>
<span class="c"># Author: Alex Ksikes </span>

<span class="c"># TODO: </span>
<span class="c"># - setup SPF for sendmail and </span>
<span class="c"># - emailerrors should be sent from same domain</span>
<span class="c"># - clean up schema.sql</span>
<span class="c"># - because of a bug in webpy unicode search fails (see models/sql_search.py)</span>

<span class="kn">import</span> <span class="nn">web</span>
<span class="kn">import</span> <span class="nn">config</span>
<span class="kn">import</span> <span class="nn">app.controllers</span>

<span class="kn">from</span> <span class="nn">app.helpers</span> <span class="kn">import</span> <span class="n">custom_error</span>

<span class="kn">import</span> <span class="nn">forum</span>

<span class="n">urls</span> <span class="o">=</span> <span class="p">(</span>        
    <span class="c"># front page</span>
    <span class="s">&#39;/&#39;</span><span class="p">,</span>                                    <span class="s">&#39;app.controllers.base.index&#39;</span><span class="p">,</span>
    <span class="s">&#39;/page/([0-9]+)/&#39;</span><span class="p">,</span>                      <span class="s">&#39;app.controllers.base.list&#39;</span><span class="p">,</span>

    <span class="c"># view, add a comment, vote</span>
    <span class="s">&#39;/module/([0-9]+)/&#39;</span><span class="p">,</span>                    <span class="s">&#39;app.controllers.module.show&#39;</span><span class="p">,</span>
    <span class="s">&#39;/module/([0-9]+)/comment/&#39;</span><span class="p">,</span>            <span class="s">&#39;app.controllers.module.comment&#39;</span><span class="p">,</span>
    <span class="s">&#39;/module/([0-9]+)/vote/&#39;</span><span class="p">,</span>               <span class="s">&#39;app.controllers.module.vote&#39;</span><span class="p">,</span>

    <span class="c"># submit a module</span>
    <span class="s">&#39;/submit/&#39;</span><span class="p">,</span>                             <span class="s">&#39;app.controllers.submit.submit&#39;</span><span class="p">,</span>

    <span class="c"># view author page</span>
    <span class="s">&#39;/author/(.*?)/&#39;</span><span class="p">,</span>                       <span class="s">&#39;app.controllers.author.show&#39;</span><span class="p">,</span>      

    <span class="c"># search browse by tag name</span>
    <span class="s">&#39;/search/&#39;</span><span class="p">,</span>                             <span class="s">&#39;app.controllers.search.search&#39;</span><span class="p">,</span>    
    <span class="s">&#39;/tag/(.*?)/&#39;</span><span class="p">,</span>                          <span class="s">&#39;app.controllers.search.list_by_tag&#39;</span><span class="p">,</span>

    <span class="c"># view tag clouds</span>
    <span class="s">&#39;/tags/&#39;</span><span class="p">,</span>                               <span class="s">&#39;app.controllers.cloud.tag_cloud&#39;</span><span class="p">,</span>
    <span class="s">&#39;/authors/&#39;</span><span class="p">,</span>                            <span class="s">&#39;app.controllers.cloud.author_cloud&#39;</span><span class="p">,</span>

    <span class="c"># table modules</span>
    <span class="s">&#39;/modules/(?:by-(.*?)/)?([0-9]+)?/?&#39;</span><span class="p">,</span>   <span class="s">&#39;app.controllers.all_modules.list_by&#39;</span><span class="p">,</span>

    <span class="c"># static pages</span>
    <span class="s">&#39;/feedback/&#39;</span><span class="p">,</span>                           <span class="s">&#39;app.controllers.feedback.send&#39;</span><span class="p">,</span>
    <span class="s">&#39;/about/&#39;</span><span class="p">,</span>                              <span class="s">&#39;app.controllers.base.about&#39;</span><span class="p">,</span>
    <span class="s">&#39;/help/&#39;</span><span class="p">,</span>                               <span class="s">&#39;app.controllers.base.help&#39;</span><span class="p">,</span>

    <span class="c"># let lighttpd handle in production</span>
    <span class="s">&#39;/(?:css|img|js|rss)/.+&#39;</span><span class="p">,</span>               <span class="s">&#39;app.controllers.public.public&#39;</span><span class="p">,</span>

    <span class="c"># canonicalize /urls to /urls/</span>
    <span class="s">&#39;/(.*[^/])&#39;</span><span class="p">,</span>                            <span class="s">&#39;app.controllers.public.redirect&#39;</span><span class="p">,</span>

    <span class="c"># mini forum app</span>
    <span class="s">&#39;/forum&#39;</span><span class="p">,</span>                               <span class="n">forum</span><span class="o">.</span><span class="n">app</span><span class="p">,</span>    

    <span class="s">&#39;/hello/(.*)&#39;</span><span class="p">,</span>                            <span class="s">&#39;hello&#39;</span><span class="p">,</span>

    <span class="c"># site admin app</span>
<span class="c">#    &#39;/admin&#39;,                              admin.app,    </span>
<span class="p">)</span>

<span class="n">app</span> <span class="o">=</span> <span class="n">web</span><span class="o">.</span><span class="n">application</span><span class="p">(</span><span class="n">urls</span><span class="p">,</span> <span class="nb">globals</span><span class="p">())</span>
<span class="n">custom_error</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">app</span><span class="p">)</span>

<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">&quot;__main__&quot;</span><span class="p">:</span>
    <span class="n">app</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
</code></pre></div>
<p>可以看出，这是 application 部分的入口，这个模块仅仅是定义了各个请求的处理方式，
并完成程序的启动，所有的实现均不在这里出现，而是通过 <code>import</code> 导入，特别需要
注意 <code>urls</code> 最后定义的 <code>/forum</code> 和 <code>/admin</code> 使用了子程序，而不是通过之前的字符串
实现映射。还需要注意对静态文件,即css,js,img,rss文件的单独处理。</p>

<p>所有这些都与之前分析过的那些小项目不同，回想起我之前写的 
<a href="https://github.com/minixalpha/BlogSystem">BlogSystem</a>, 所有的处理实现都放在
同一个文件中，导致最后一个文件居然 700多行，真是让人潸然泪下。。。
而且之前也不知道使用子程序，所有处理都堆在一起。看来读完这份源代码，真应该重构一
下了。</p>

<h3>app 模块</h3>
<div class="highlight"><pre><code class="text language-text" data-lang="text">app/
    ├── models # 数据模块，MVC中的 M
    ├── views # 显示模块，MVC中的 V
    ├── controllers # 控制模块，MVC中的 C
    └── helpers # 辅助模块，实现辅助功能

4 directories
</code></pre></div>
<h3>controllers 模块</h3>
<div class="highlight"><pre><code class="text language-text" data-lang="text">controllers/
    ├── base.py         # 对基本页面，如网站主页,关于网页，帮助等的处理
    ├── all_modules.py  # 显示全部模块
    ├── module.py       # 对模块页面的处理
    ├── search.py       # 对搜索模块功能处理
    ├── submit.py       # 对提交模块的处理
    ├── author.py       # 查看模块作者信息
    ├── cloud.py        # 对标签云页面进行处理
    ├── feedback.py     # 处理反馈信息
    └── public.py       # 对静态文件的处理
</code></pre></div>
<p>这个模块主要是对请求处理的实现，在 <code>urls</code> 里定义的那些映射关系，
很多被映射到这里。</p>

<p>实现过程中，调用 models 模块对数据操作，再送入 views 模块通过模板引擎显示数据内容。</p>

<h3>models 模块</h3>
<div class="highlight"><pre><code class="text language-text" data-lang="text">models/
    ├── comments.py     # 对评论数据的处理
    ├── modules.py      # 对模块数据的处理
    ├── rss.py          # 对 rss 订阅的处理
    ├── sql_search.py   # 对搜索的数据处理
    ├── submission.py   # 对用户提交内容的处理
    ├── tags.py         # 对标签内容的数据处理
    └── votes.py        # 对用户投票的数据处理
</code></pre></div>
<p>这个模块直接调用 web.py 的db模块对数据库进行操作，对数据库的连接在 config.py 中
已经完成。这里完成数据的获取，处理，返回。可以看出，对不同种类的数据又分成了
很多小的模块。</p>

<h3>views 模块</h3>
<div class="highlight"><pre><code class="text language-text" data-lang="text">views/
    ├── about.html
    ├── all_modules.html
    ├── faq.html
    ├── feedback.html
    ├── help.html
    ├── internal_error.html
    ├── layout.html
    ├── list_modules_by_author.html
    ├── list_modules.html
    ├── not_found.html
    ├── rss.html
    ├── show_module.html
    ├── submit_module.html
    ├── submitted_form.html
    ├── tag_cloud_author.html
    └── tag_cloud.html
</code></pre></div>
<p>这个模块是各个页面的模板文件所在，通过模板引擎，这些模板的内容
被填充，最终返回给用户。</p>

<h3>helpers 模块</h3>
<div class="highlight"><pre><code class="text language-text" data-lang="text">helpers/
    ├── custom_error.py     # 错误处理，如未找到页面等等
    ├── formatting.py       # 格式处理相关的工具函数
    ├── image.py            # 图片处理相关函数
    ├── misc.py             # 用于生成一些随机数据
    ├── paging.py           # 用于多条信息分成多个页面
    ├── render.py           # 对基本布局的包装
    ├── strip_html.py       # HTML 文件的处理
    ├── tag_cloud.py        # 对标签云的处理
    └── utils.py            # 常用的小工具函数
</code></pre></div>
<p>这一模块把需要用到的一些工具类函数分成多个类组织在一起，
这样可以避免之前的 MVC 模块太臃肿。</p>

<h2>forum 模块</h2>

<p>在 <code>application.py</code> 中定义了对论坛请求的处理，这是在子程序中进行处理的。</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">    # mini forum app
    &#39;/forum&#39;,                               forum.app,    
</code></pre></div>
<p>对 <code>/forum</code> 的请求会发到子程序 <code>forum.py</code>, 这是一个类似 <code>application.py</code>
的程序：</p>
<div class="highlight"><pre><code class="python language-python" data-lang="python"><span class="c">#!/usr/bin/env python</span>
<span class="c"># Author: Alex Ksikes </span>

<span class="kn">import</span> <span class="nn">web</span>
<span class="kn">import</span> <span class="nn">config</span>
<span class="kn">import</span> <span class="nn">app_forum.controllers</span>

<span class="kn">from</span> <span class="nn">app.helpers</span> <span class="kn">import</span> <span class="n">custom_error</span>

<span class="n">urls</span> <span class="o">=</span> <span class="p">(</span>
    <span class="s">&#39;/&#39;</span><span class="p">,</span>                             <span class="s">&#39;app_forum.controllers.base.index&#39;</span><span class="p">,</span>
    <span class="s">&#39;/new/&#39;</span><span class="p">,</span>                         <span class="s">&#39;app_forum.controllers.base.new&#39;</span><span class="p">,</span>
    <span class="s">&#39;/page/([0-9]+)/&#39;</span><span class="p">,</span>               <span class="s">&#39;app_forum.controllers.base.list&#39;</span><span class="p">,</span>
    <span class="s">&#39;/thread/([0-9]+)/&#39;</span><span class="p">,</span>             <span class="s">&#39;app_forum.controllers.base.show&#39;</span><span class="p">,</span>
    <span class="s">&#39;/thread/([0-9]+)/reply/&#39;</span><span class="p">,</span>       <span class="s">&#39;app_forum.controllers.base.reply&#39;</span><span class="p">,</span>
<span class="p">)</span>

<span class="n">app</span> <span class="o">=</span> <span class="n">web</span><span class="o">.</span><span class="n">application</span><span class="p">(</span><span class="n">urls</span><span class="p">,</span> <span class="nb">globals</span><span class="p">())</span>
<span class="n">custom_error</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">app</span><span class="p">)</span>

<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">&quot;__main__&quot;</span><span class="p">:</span>
    <span class="n">app</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
</code></pre></div>
<p>可以看出子程序的作用。在 application 中没有定义　<code>/forum/new/</code> ，但是对
<code>/forum</code> 的请求会发给这里的 <code>forum.py</code>, 这个程序定义了对 <code>/new</code> 的处理，
它们连起来，构成了对 <code>/forum/new/</code> 处理的映射。</p>

<p>forum 模块的结构与 application 模块是相似的:</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">app_forum/
    ├── controllers
    │   ├── base.py         # 对基本页面，如论坛主页的处理
    ├── models
    │   └── threads.py      # 对论坛数据的处理
    └── views               # 论坛页面模板
        ├── list_threads.html
        ├── show_thread.html
        └── submitted_form.html
</code></pre></div>
<p>可以看出，只有很少的文件, 只搭了个框架，具体的内容我们就不分析了。</p>


      </article>

      <div class="comments">
        
          <div id="disqus_thread"></div>
 <script type="text/javascript">
     /* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */
     var disqus_shortname = 'minixalpha'; // required: replace example with your forum shortname

     /* * * DON'T EDIT BELOW THIS LINE * * */
     (function() {
         var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
         dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
         (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
     })();
 </script>
 <noscript>Please enable JavaScript to view the <a href="http://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
    <a href="http://disqus.com" class="dsq-brlink">comments powered by <span class="logo-disqus">Disqus</span></a>
    

        
      </div>


      <footer>
        Copyright (c) minixalpha 2014
      </footer>

    </div>
  </body>
</html>
